#include "event/znx_openssl.h"
#include "event/znx_tcp_proc.h"
#include "event/znx_worker_proc.h"


int task = 20;

znx_ssl_conf_t  *ssl_conf;
znx_ssl_conf_t  *ssl_client_conf;

static const char *cert =
    "-----BEGIN CERTIFICATE-----\n"
    "MIID8DCCAtigAwIBAgIJALL9eJPZ6neGMA0GCSqGSIb3DQEBBQUAMFgxCzAJBgNV\n"
    "BAYTAkdCMQ0wCwYDVQQIEwRUZXN0MQ0wCwYDVQQHEwRUZXN0MQ0wCwYDVQQKEwRU\n"
    "ZXN0MQ0wCwYDVQQLEwRUZXN0MQ0wCwYDVQQDEwR0ZXN0MB4XDTE1MTAyMTE2MjQ1\n"
    "NloXDTE1MTEyMDE2MjQ1NlowWDELMAkGA1UEBhMCR0IxDTALBgNVBAgTBFRlc3Qx\n"
    "DTALBgNVBAcTBFRlc3QxDTALBgNVBAoTBFRlc3QxDTALBgNVBAsTBFRlc3QxDTAL\n"
    "BgNVBAMTBHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDz/AoE\n"
    "c+TPdm+Aqcchq8fLNWksFQZqbsCBGnq8rUG1b6MsVlAOkDUQGRlNPs9v0/+pzgX7\n"
    "IYXPCFcV7YONNsTUfvBYTq43mfOycmAdb3SX6kBygxdhYsDRZR+vCAIkjoRmRB20\n"
    "meh1motqM58spq3IcT8VADTRJl1OI48VTnxmXdCtmkOymU948DcauMoxm03eL/hU\n"
    "6eniNEujbnbB305noNG0W5c3h6iz9CvqUAD1kwyjick+f1atB2YYn1bymA+db6YN\n"
    "3iTo0v2raWmIc7D+qqpkNaCRxgMb2HN6X3/SfkijtNJidjqHMbs2ftlKJ5/lODPZ\n"
    "rCPQOcYK6TT8MIZ1AgMBAAGjgbwwgbkwHQYDVR0OBBYEFFUC1GrAhUp7IvJH5iyf\n"
    "+fJQliEIMIGJBgNVHSMEgYEwf4AUVQLUasCFSnsi8kfmLJ/58lCWIQihXKRaMFgx\n"
    "CzAJBgNVBAYTAkdCMQ0wCwYDVQQIEwRUZXN0MQ0wCwYDVQQHEwRUZXN0MQ0wCwYD\n"
    "VQQKEwRUZXN0MQ0wCwYDVQQLEwRUZXN0MQ0wCwYDVQQDEwR0ZXN0ggkAsv14k9nq\n"
    "d4YwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAtaUQOr3Qn87KXmmP\n"
    "GbSvCLSl+bScE09VYZsYaB6iq0pGN9y+Vh4/HjBUUsFexopw1dY25MEEJXEVi1xV\n"
    "2krLYAsfKCM6c1QBVmdqfVuxUvxpXwr+CNRNAlzz6PhjkeY/Ds/j4sg7EqN8hMmT\n"
    "gu8GuogX7+ZCgrzRSMMclWej+W8D1xSIuCC+rqv4w9SZdtVb3XGpCyizpTNsQAuV\n"
    "ACXvq9KXkEEj+XNvKrNdWd4zG715RdMnVm+WM53d9PLp63P+4/kwhwHULYhXygQ3\n"
    "DzzVPaojBBdw3VaHbbPHnv73FtAzOb7ky6zJ01DlmEPxEahCFpklMkY9T2uCdpj9\n"
    "oOzaNA==\n"
    "-----END CERTIFICATE-----\n";

static const char *key =
    "-----BEGIN RSA PRIVATE KEY-----\n"
    "MIIEowIBAAKCAQEA8/wKBHPkz3ZvgKnHIavHyzVpLBUGam7AgRp6vK1BtW+jLFZQ\n"
    "DpA1EBkZTT7Pb9P/qc4F+yGFzwhXFe2DjTbE1H7wWE6uN5nzsnJgHW90l+pAcoMX\n"
    "YWLA0WUfrwgCJI6EZkQdtJnodZqLajOfLKatyHE/FQA00SZdTiOPFU58Zl3QrZpD\n"
    "splPePA3GrjKMZtN3i/4VOnp4jRLo252wd9OZ6DRtFuXN4eos/Qr6lAA9ZMMo4nJ\n"
    "Pn9WrQdmGJ9W8pgPnW+mDd4k6NL9q2lpiHOw/qqqZDWgkcYDG9hzel9/0n5Io7TS\n"
    "YnY6hzG7Nn7ZSief5Tgz2awj0DnGCuk0/DCGdQIDAQABAoIBAGjKc7L94+SHRdTJ\n"
    "FtILacCJrCZW0W6dKulIajbnYzV+QWMlnzTiEyha31ciBw5My54u8rqt5z7Ioj60\n"
    "yK+6OkfaTXhgMsuGv/iAz29VE4q7/fow+7XEKHTHLhiLJAB3hb42u1t6TzFTs1Vl\n"
    "3pPa8wEIQsPOVuENzT1mYGoST7PW+LBIMr9ScMnRHfC0MNdV/ntQiXideOAd5PkA\n"
    "4O7fNgYZ8CTAZ8rOLYTMFF76/c/jLiqfeghqbIhqMykk36kd7Lud//FRykVsn1aJ\n"
    "REUva/SjVEth5kITot1hpMC4SIElWpha2YxiiZFoSXSaUbtHpymiUGV01cYtMWk0\n"
    "MZ5HN3ECgYEA/74U8DpwPxd4up9syKyNqOqrCrYnhEEC/tdU/W5wECi4y5kppjdd\n"
    "88lZzICVPzk2fezYXlCO9HiSHU1UfcEsY3u16qNCvylK7Qz1OqXV/Ncj59891Q5Z\n"
    "K0UBcbnrv+YD6muZuhlHEbyDPqYO091G9Gf/BbL5JIBDzg1qFO9Dh9cCgYEA9Drt\n"
    "O9PJ5Sjz3mXQVtVHpwyhOVnd7CUv8a1zkUQCK5uQeaiF5kal1FIo7pLOr3KAvG0C\n"
    "pXbm/TobwlfAfcERQN88aPN8Z/l1CB0oKV6ipBMD2/XLzDRtx8lpTeh/BB8jIhrz\n"
    "+FDJY54HCzLfW0P5kT+Cyw51ofjziPnFdO/Z6pMCgYEAon17gEchGnUnWCwDSl2Y\n"
    "hELV+jBSW02TQag/b+bDfQDiqTnfpKR5JXRBghYQveL0JH5f200EB4C0FboUfPJH\n"
    "6c2ogDTLK/poiMU66tCDbeqj/adx+fTr4votOL0QdRUIV+GWAxAcf8BvA1cvBJ4L\n"
    "fy60ckKM2gxFCJ6tUC/VkHECgYBoMDNAUItSnXPbrmeAg5/7naGxy6qmsP6RBUPF\n"
    "9tNOMyEhJUlqAT2BJEOd8zcFFb3hpEd6uwyzfnSVJcZSX2iy2gj1ZNnvqTXJ7lZR\n"
    "v7N2dz4wOd1lEgC7OCsaN1LoOThNtl3Z0uz2+FVc66jpUEhJNGThpxt7q66JArS/\n"
    "vAqkzQKBgFkzqA6QpnH5KhOCoZcuLQ4MtvnNHOx1xSm2B0gKDVJzGkHexTmOJvwM\n"
    "ZhHXRl9txS4icejS+AGUXNBzCWEusfhDaZpZqS6zt6UxEjMsLj/Te7z++2KQn4t/\n"
    "aI77jClydW1pJvICtqm5v+sukVZvQTTJza9ujta6fj7u2s671np9\n"
    "-----END RSA PRIVATE KEY-----\n";

static znx_str_t session_ctx = {
    .data = (u_char *)"test",
    .len = sizeof("test") - 1
};


static znx_str_t ticket_key = {
    .data = (u_char *)"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    .len = 48
};

static znx_str_t sni = {
    .data = (u_char *)"www.aaa.com",
    .len = sizeof("www.aaa.com") - 1
};


void ssl_conf_init()
{
    znx_ssl_session_cache_init(1000);

    char            *err;
    ssl_conf = znx_ssl_conf_create(ZNX_SSL_TLSv1_1|ZNX_SSL_TLSv1_2,
        NULL, &err);
    if (!ssl_conf) {
        ZNX_DEF_LOG_FATAL(NULL, "znx_ssl_conf_create() failed, err: %s", err);
    }

    ssl_client_conf = znx_ssl_conf_create(ZNX_SSL_TLSv1_1|ZNX_SSL_TLSv1_2,
        NULL, &err);
    if (!ssl_client_conf) {
        ZNX_DEF_LOG_FATAL(NULL, "znx_ssl_conf_create() failed, err: %s", err);
    }

    STACK_OF(X509)  *chain;
    chain = znx_parse_pem_cert((const u_char *)cert, strlen(cert), &err);
    if (!chain) {
        ZNX_DEF_LOG_FATAL(NULL, "znx_parse_pem_cert() failed, err: %s", err);
    }

    // After calling znx_ssl_conf_set_cert, the chain will be managerd by ssl_conf
    // and released after ssl_conf is destroyed.
    if (!znx_ssl_conf_set_cert(ssl_conf, chain, &err)) {
        ZNX_DEF_LOG_FATAL(NULL, "znx_ssl_conf_set_cert() failed, err: %s", err);
    }

    EVP_PKEY        *pkey;
    pkey = znx_parse_pem_priv_key((const u_char *)key, strlen(key), NULL, &err);
    if (!pkey) {
        ZNX_DEF_LOG_FATAL(NULL, "znx_parse_pem_priv_key() failed, err: %s", err);
    }

    if (!znx_ssl_conf_set_priv_key(ssl_conf, pkey, &err)) {
        ZNX_DEF_LOG_FATAL(NULL, "znx_ssl_conf_set_priv_key() failed, err: %s", err);
    }

    znx_free_priv_key(pkey);

    if (!znx_ssl_session_cache(ssl_conf, &session_ctx,
        ZNX_TRUE, 1000000, &err))
    {
        ZNX_DEF_LOG_FATAL(NULL, "znx_ssl_session_cache() failed, err: %s", err);
    }

    if (!znx_ssl_session_ticket_key(ssl_conf, &ticket_key, &err)) {
        ZNX_DEF_LOG_FATAL(NULL, "znx_ssl_session_ticket_key() failed, err: %s", err);
    }
}


#if 0
static void
znx_test_server_ssl_shutdown_handler(znx_conn_t *c)
{
    znx_event_loop_remove_conn(c);
    znx_conn_free(c);
}
#endif

typedef struct znx_test_sm_s    znx_test_sm_t;

typedef void (*znx_test_sm_event_handle)(znx_test_sm_t *sm);


struct znx_test_sm_s {
    int                         data;
    znx_conn_t                  *c;
    znx_test_sm_event_handle    read_handle;
    znx_test_sm_event_handle    write_handle;
    unsigned                    read_active:1;
    unsigned                    write_active:1;
};


void znx_test_sm_enable_read(znx_test_sm_t *sm)
{
    sm->read_active = 1;
    znx_event_loop_enable_read(sm->c);
}

void znx_test_sm_disable_read(znx_test_sm_t *sm)
{
    sm->read_active = 0;
    // disable操作由znx_http_event_handle调用.
    // znx_event_loop_enable_read(r->c);
}

void znx_test_sm_enable_write(znx_test_sm_t *sm)
{
    sm->write_active = 1;
    znx_event_loop_enable_write(sm->c);
}

void znx_test_sm_disable_write(znx_test_sm_t *sm)
{
    sm->write_active = 0;
    // disable操作由znx_http_event_handle调用.
    // znx_event_loop_enable_write(r->c);
}


void znx_test_event_handle(znx_event_t *ev)
{
    znx_test_sm_t       *sm;
    znx_conn_t          *c;

    c = ev->data;
    sm = c->data;

    if (ev == &c->read) {
        if (sm->read_active) {
            sm->read_handle(sm);
            return;
        }

        znx_event_loop_disable_read(c);
        return;
    }

    if (sm->write_active) {
        sm->write_handle(sm);
        return;
    }

    znx_event_loop_disable_write(c);
}


static void
znx_test_conn_close(znx_conn_t *c)
{
    if (c->ssl) {
        if (znx_ssl_shutdown(c) == ZNX_AGAIN) {
            c->ssl->handler = znx_test_conn_close;
            return;
        }
    }

    znx_event_loop_remove_conn(c);
    znx_conn_free(c);
}


static void
znx_test_sm_server_read(znx_test_sm_t *sm)
{
    znx_result_t            res, res1;
    u_char                  buffer[1024];

    znx_conn_t              *c = sm->c;

    for ( ;; ) {
        res = znx_ssl_read(c, buffer, 1000);
        if (res == ZNX_AGAIN) {
            znx_test_sm_enable_read(sm);
            return;
        }

        if (res == 0) {
            ZNX_DEF_LOG_DEBUG(NULL, "server ssl read eof");
            break;
        }

        buffer[res] = '\0';
        ZNX_DEF_LOG_ERROR(NULL, "server read %s", buffer);

        res1 = znx_ssl_write(c, buffer, (size_t)res);
        if (res1 != res) {
            ZNX_DEF_LOG_FATAL(NULL, "expet %d, but got %d", res, res1);
            break;
        }
    }

    ZNX_DEF_LOG_DEBUG(NULL, "server ssl close");
    znx_thread_free(sm);
    znx_test_conn_close(c);
}



static void
znx_test_sm_init(znx_test_sm_t *sm, znx_conn_t *c)
{
    sm->c = c;
    c->data = sm;
    sm->c->read.handler = znx_test_event_handle;
    sm->c->write.handler = znx_test_event_handle;
    sm->read_handle = znx_test_sm_server_read;
    znx_test_sm_server_read(sm);
}


static int
znx_test_ssl_client_hello_handler(SSL *ssl, int *al, void *arg)
{
    znx_conn_t  *conn;
    conn = znx_ssl_get_conn(ssl);
    znx_str_t hostname = ZNX_NULL_STRING;
    char *err;

    if (!znx_ssl_get_client_hello_server_name(conn, &hostname, &err)) {
        ZNX_DEF_LOG_ERROR(NULL, "znx_ssl_get_client_hello_server_name() failed, err: %s", err);
        return SSL_CLIENT_HELLO_ERROR;
    }

    ZNX_DEF_LOG_DEBUG(NULL, "hostname: %v", &hostname);

    if (hostname.len != sni.len || znx_strncmp(hostname.data, sni.data, sni.len) != 0) {
        ZNX_DEF_LOG_FATAL(NULL, "expect %v, but got %v", &sni, &hostname);
    }

    return SSL_CLIENT_HELLO_SUCCESS;
}


static int
znx_test_ssl_servername_handler(SSL *ssl, int *ad, void *args)
{
    znx_conn_t  *conn;
    conn = znx_ssl_get_conn(ssl);
    znx_str_t hostname = ZNX_NULL_STRING;
    char *err;

    // https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_tlsext_servername_callback.html
    if (!znx_ssl_get_server_name(conn, &hostname, &err)) {
        ZNX_DEF_LOG_ERROR(NULL, "znx_ssl_get_server_name() failed, err: %s", err);
        return SSL_TLSEXT_ERR_ALERT_FATAL;
    }

    ZNX_DEF_LOG_DEBUG(NULL, "hostname: %v", &hostname);

    if (hostname.len != sni.len || znx_strncmp(hostname.data, sni.data, sni.len) != 0) {
        ZNX_DEF_LOG_FATAL(NULL, "expect %v, but got %v", &sni, &hostname);
    }

    return SSL_TLSEXT_ERR_OK;
}


static void
znx_test_ssl_handshake_handler(znx_conn_t *c)
{
    if (c->ssl->handshaked) {
        /*
         * The majority of browsers do not send the "close notify" alert.
         * Among them are MSIE, old Mozilla, Netscape 4, Konqueror,
         * and Links.  And what is more, MSIE ignores the server's alert.
         *
         * Opera and recent Mozilla send the alert.
         */
        c->ssl->no_wait_shutdown = 1;

        znx_test_sm_t *sm = znx_thread_calloc(sizeof(znx_test_sm_t));
        znx_test_sm_init(sm, c);

        return;
    }

    ZNX_DEF_LOG_ERROR(NULL, "ssl handshake failed");
    znx_test_conn_close(c);
}


static void
znx_test_ssl_handshake(znx_event_t *rev)
{
    znx_conn_t  *server_conn = rev->data;
    char        *err;

    if (rev->timedout) {
        ZNX_DEF_LOG_ERROR(NULL, "client timed out");
        znx_event_loop_remove_conn(server_conn);
        znx_conn_free(server_conn);
        return;
    }

    if (!znx_ssl_create_connection(ssl_conf, server_conn, ZNX_FALSE, &err))
    {
        ZNX_DEF_LOG_FATAL(NULL, "znx_ssl_create_connection failed, err: %s", err);
    }

    znx_result_t  res = znx_ssl_handshake(server_conn);
    if (res == ZNX_AGAIN) {
        server_conn->ssl->handler = znx_test_ssl_handshake_handler;
        znx_event_loop_add_timer(rev, 1000);
        return;
    }

    znx_test_ssl_handshake_handler(server_conn);
}


void znx_test_server_accept_handler(znx_event_t *event)
{
    znx_conn_t  *server_conn = event->data;
    znx_event_loop_start_io(server_conn);
    znx_test_ssl_handshake(event);
}


u_char  *str1 = (u_char *)"aaaaaaaaa";
size_t str1_len = sizeof("aaaaaaaaa") - 1;

u_char  *str2 = (u_char *)"bbbbbbbbb";
size_t str2_len = sizeof("bbbbbbbbb") - 1;

u_char  *str3 = (u_char *)"ccccccccc";
size_t str3_len = sizeof("ccccccccc") - 1;


void znx_test_sm_client_read_3(znx_test_sm_t *sm)
{
    znx_conn_t                  *c = sm->c;
    znx_result_t                res;

    char buf[1024];

    res = znx_ssl_read(c, buf, 1000);
    if (res == ZNX_AGAIN) {
        sm->read_handle = znx_test_sm_client_read_3;
        znx_test_sm_enable_read(sm);
        return;
    }

    if (res < 0 || res == 0) {
        ZNX_DEF_LOG_FATAL(NULL, "expect get %s, but got %d", str3, res);
    }

    if ((size_t)res != str3_len || znx_strncmp(buf, str3, str3_len) != 0) {
        ZNX_DEF_LOG_FATAL(NULL, "expect get %s, but got other", str3);
    }

    ZNX_DEF_LOG_DEBUG(NULL, "client ssl close");
    znx_thread_free(sm);
    znx_test_conn_close(c);
}


void znx_test_sm_client_read_2(znx_test_sm_t *sm)
{
    znx_conn_t                  *c = sm->c;
    znx_result_t                res;

    char buf[1024];

    res = znx_ssl_read(c, buf, 1000);
    if (res == ZNX_AGAIN) {
        sm->read_handle = znx_test_sm_client_read_2;
        znx_test_sm_enable_read(sm);
        return;
    }

    if (res < 0 || res == 0) {
        ZNX_DEF_LOG_FATAL(NULL, "expect get %s, but got %d", str2, res);
    }

    if ((size_t)res != str2_len || znx_strncmp(buf, str2, str2_len) != 0) {
        ZNX_DEF_LOG_FATAL(NULL, "expect get %s, but got other", str2);
    }


    res = znx_ssl_write(c, str3, str3_len);
    if ((size_t)res != str3_len) {
        ZNX_DEF_LOG_FATAL(NULL, "expect %d, but got %d", str3_len, res);
    }

    znx_test_sm_client_read_3(sm);
}


void znx_test_sm_client_read_1(znx_test_sm_t *sm)
{
    znx_conn_t                  *c = sm->c;
    znx_result_t                res;

    char buf[1024];

    res = znx_ssl_read(c, buf, 1000);
    if (res == ZNX_AGAIN) {
        sm->read_handle = znx_test_sm_client_read_1;
        znx_test_sm_enable_read(sm);
        return;
    }

    if (res < 0 || res == 0) {
        ZNX_DEF_LOG_FATAL(NULL, "expect get %s, but got %d", str1, res);
    }

    if ((size_t)res != str1_len || znx_strncmp(buf, str1, str1_len) != 0) {
        ZNX_DEF_LOG_FATAL(NULL, "expect get %s, but got other", str1);
    }


    res = znx_ssl_write(c, str2, str2_len);
    if ((size_t)res != str2_len) {
        ZNX_DEF_LOG_FATAL(NULL, "expect %d, but got %d", str2_len, res);
    }

    znx_test_sm_client_read_2(sm);
}


void znx_test_sm_client_read(znx_test_sm_t *sm)
{
    znx_result_t                res;
    znx_conn_t                  *c = sm->c;

    res = znx_ssl_write(c, str1, str1_len);
    if ((size_t)res != str1_len) {
        ZNX_DEF_LOG_FATAL(NULL, "expect %d, but got %d", str1_len, res);
    }

    znx_test_sm_client_read_1(sm);

}


void znx_test_sm_client_init(znx_test_sm_t *sm, znx_conn_t *c)
{
    sm->data = (int)(intptr_t)c->data;
    sm->c = c;
    c->data = sm;
    sm->c->read.handler = znx_test_event_handle;
    sm->c->write.handler = znx_test_event_handle;
    sm->read_handle = znx_test_sm_client_read;
    znx_test_sm_client_read(sm);
}



void znx_test_client_ssl_handshake_handler(znx_conn_t *c)
{
    if (c->ssl->handshaked) {
        /*
         * The majority of browsers do not send the "close notify" alert.
         * Among them are MSIE, old Mozilla, Netscape 4, Konqueror,
         * and Links.  And what is more, MSIE ignores the server's alert.
         *
         * Opera and recent Mozilla send the alert.
         */
        c->ssl->no_wait_shutdown = 1;

        znx_test_sm_t *sm = znx_thread_calloc(sizeof(znx_test_sm_t));
        znx_test_sm_client_init(sm, c);
        return;
    }

    ZNX_DEF_LOG_ERROR(NULL, "ssl handshake failed");
    znx_test_conn_close(c);
}



void znx_test_client_conn_handler(void *data, znx_conn_t *conn, znx_result_t res)
{
    char            *err;

    if (res != ZNX_SUCCESS) {
        ZNX_DEF_LOG_FATAL(NULL, "expect connect success, but got other");
    }

    conn->data = data;
    znx_event_loop_start_io(conn);

    if (!znx_ssl_create_connection(ssl_client_conf, conn, ZNX_TRUE, &err)) {
        ZNX_DEF_LOG_FATAL(NULL, "znx_ssl_create_connection failed, err: %s", err);
    }

    SSL_set_tlsext_host_name(conn->ssl->ssl, "www.aaa.com");

    res = znx_ssl_handshake(conn);
    if (res == ZNX_AGAIN) {
        conn->ssl->handler = znx_test_client_ssl_handshake_handler;
        znx_event_loop_add_timer(&conn->read, 1000);
        return;
    }

    znx_test_client_ssl_handshake_handler(conn);
}


void znx_test_client_handler(znx_dispatch_event_t *dispatch_event)
{
    znx_thread_free(dispatch_event);

    znx_address_t address;
    bzero(&address, sizeof(znx_address_t));
    znx_conn_t *conn = znx_thread_calloc(sizeof(znx_conn_t));
    znx_conn_init(conn, ZNX_CONN_EPOLL_ET);

    if (!znx_address_parser(
        (u_char *)"127.0.0.1:8889",
        strlen("127.0.0.1:8889"),
        &address, NULL))
    {
        ZNX_DEF_LOG_FATAL(NULL, "parse 126.0.0.1:8889 failed");
    }

    znx_tcp_connect_setup(conn, &address, NULL, NULL, 20,
        znx_test_client_conn_handler, NULL);
}


void znx_tls_test()
{
    znx_dispatch_event_t            *dispatch_event;

    for (int i = 0; i < task; i++) {
        dispatch_event = znx_thread_calloc(sizeof(znx_dispatch_event_t));
        dispatch_event->handler = znx_test_client_handler;
        znx_worker_processor_post_event(dispatch_event);
    }
}


int main()
{
    znx_proc_init();

    znx_address_t   address;
    bzero(&address, sizeof(znx_address_t));
    if (!znx_address_parser(
        (u_char *)"127.0.0.1:8889", strlen("127.0.0.1:8889"), &address, NULL)) {
        ZNX_DEF_LOG_FATAL(NULL, "parser %s failed", "127.0.0.1:8889");
    }

    znx_listener_t *l = znx_listener_alloc();
    l->listen_addr = "127.0.0.1:8889";
    l->address = address;
    l->backlog = 1024;
    l->nodelay = 1;
    l->reuseaddr = 1;
    l->accept_handler = znx_test_server_accept_handler;

    if (!znx_listener_open(l, INVALID_FD)) {
        ZNX_DEF_LOG_FATAL(NULL, "open listener 127.0.0.1:8889 failed");
    }

    znx_rwlock_wlock(&g_listeners->rwlock);
    znx_queue_insert_tail(&g_listeners->head, &l->queue);
    g_listeners->version++;
    znx_rwlock_wunlock(&g_listeners->rwlock);

    ssl_conf_init();

    SSL_CTX_set_client_hello_cb(ssl_conf->ctx,
        znx_test_ssl_client_hello_handler,
        NULL);

    SSL_CTX_set_tlsext_servername_callback(ssl_conf->ctx,
        znx_test_ssl_servername_handler);

    znx_worker_processor_start(4, 2, 0);
    znx_tcp_accepter_start(2);

    znx_tls_test();
    usleep(50000);

    znx_proc_shut_down();

    znx_ssl_conf_destroy(ssl_conf);
    znx_ssl_conf_destroy(ssl_client_conf);

    return 0;
}

